1.1 Is Unique: Implement an algorithm to determine if a string has all unique characters. What if you cannot use additional data structures?


In [43]:
# from IPython.core.debugger import set_trace

def has_unique_characters(str=''):
    '''
    Checks if a string has all unique characters
    
    >>> has_unique_characters('abc')
    True
    >>> has_unique_characters()
    True
    >>> has_unique_characters('abac')
    False
    '''
#     1/ time: 1.74 µs ± 14.5 ns ---------------------------
#     unique_chars = dict.fromkeys(str,1)
#     return len(unique_chars)==len(str)


#     2/ time: 1.04 µs ± 7.39 ns using dictionary ----------
#     unique_chars ={}
#     for char in str:
#         if char in unique_chars:
#             return False
#         unique_chars[char]=1
#     return True


#     3/ 1.4 µs ± 19.1 ns using a set -----------------------
#     char_set =set()
#     for char in str:
#         if char in char_set:
#             return False
#         char_set.add(char)
#     return True

#     4/ 3.16 µs ± 131 ns using a bit vector ----------------
#     bit_vector =0b0
#     pos_a=ord('a')
    
#     import pdb; pdb.set_trace()
#     for char in str:
#         pos_char=ord(char)-pos_a
#         if bit_vector & (1 << pos_char):
#             return False
#         bit_vector |= (1 << pos_char)
#     return True

#     5/ 1.88 µs ± 12.2 ns Array -----------------------------
    a =[0]*40
    pos_a=ord('a')
    
#     import pdb; pdb.set_trace()
    for char in str:
        pos_char=ord(char)-pos_a
        if a[pos_char]:
            return False
        a[pos_char] =1
    return True



# print(has_unique_characters('qwertyuuidfsdgsg'))



%timeit has_unique_characters('qwertyuuidfsdgsg')
# import doctest; doctest.testmod()


1.88 µs ± 12.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

 

 


1.2 Check Permutation: Given two strings,write a method to decide if one is a permutation of the other.


In [37]:
# def isPermutations(s1,s2):
#     len1 = len(s1)
#     len2 = len(s2)
    
#     if len1 != len2:
#         return False
    
#     ss1=sorted(s1)
#     ss2=sorted(s2)
    
#     for i in range(0,len1):
#         if ss1[i] != ss2[i]:
#             return False
#     return True


def isPermutations(s1,s2):
    if len(s1) != len(s2):
        return False

    char_counts={}

    
    # fill it    
    for c in s1:
        char_counts[c] = char_counts.get(c,0) +1

    # empty it
    for c in s2:
        char_counts[c] = char_counts.get(c,0) -1

    # check if all are 0
    for c in char_counts:
        if char_counts[c] != 0:
            return False

    return True




        
print( isPermutations('abcdefffgh', 'dcabefghff') )


True

1.3 URLify :

Write a method to replace all spaces in a string with '%20 You may assume that the string has suf cient space at the end to hold the additional characters, and that you are given the "true" length of the string. (Note: if implementing in Java, please use a character array so that you can perform this operation in place.)

EXAMPLE
Input: "Mr John Smith ", 13
Output: "Mr%20John%20Smith"

In [94]:
def urlify(ss="Mr John Smith    ", true_len=13):
#     574 ns ± 47.7 ns
#     return s.strip().replace(' ','%20')

#     760 ns ± 34 ns
#     return '%20'.join(s.strip().split(' '))
    
#     6.54 µs ± 191 ns
#     n_spaces = ss.count(' ',0,true_len)
    n_spaces = 0
    for i in range(0,true_len):
        if ss[i]==' ':
            n_spaces +=1
    s=list(ss)
    for i in range(true_len-1,0,-1):
        if s[i] != ' ':
            s[i+n_spaces*2]=s[i]
        else:
            s[i+n_spaces*2]='0'
            s[i+n_spaces*2-1]='2'
            s[i+n_spaces*2-2]='%'
            n_spaces -= 1
    return ''.join(s)


urlify()=="Mr%20John%20Smith"

# %timeit urlify()


Out[94]:
True

1.5 One Away:

There are three types of edits that can be performed on strings: insert a character, remove a character, or replace a character. Given two strings, write a function to check if they are one edit (or zero edits) away.

EXAMPLE

pale, ple -> true 
pales, pale -> true 
pale, bale -> true 
pale, bake -> false

1h


In [19]:
def first_diff(old, new):
    for i in range(0, min(  len(old), len(new)  )):
        if old[i] != new[i]:
            return i
    return i+1



def inserted1(old, new):
    if len(new)-len(old)!=1:
        return False
    
    i = first_diff(old,new)
    
    if old[i:] == new[i+1:]:
        return True
    return False

    
    return True




def deleted1(old, new):
    if len(new)-len(old)!=-1:
        return False
    
    i = first_diff(old,new)
    
    if old[i+1:] == new[i:]:
        return True
    return False


    return True



def updated1(old, new):
    if len(new)-len(old)!=0:
        return False
    
    i = first_diff(old,new)
    
    if old[i+1:] == new[i+1:]:
        return True
    return False


    
def zero_or_one(old, new):
#     if old == new :
#         return True
    
#     if inserted1(old,new) \
#     or updated1(old,new)  \
#     or deleted1(old,new):
#         return True

#     return False


#     len_diff = len(new)-len(old)
#     checks = {0:updated1, 1:inserted1, -1:deleted1}
#     return checks.get(len_diff, lambda o,n: False)(old,new)


    len_diff = len(new)-len(old)
    
    if len_diff == 0:    return updated1(old,new)
    elif len_diff ==1:   return inserted1(old,new)
    elif len_diff == -1: return deleted1(old,new)
    else:                return False


print (True, zero_or_one('pale', 'pale'))
print (True, zero_or_one('pale', 'ple'))
print (True, zero_or_one('pales', 'pale')) 
print (True, zero_or_one('pale', 'bale'))
print (False,zero_or_one('pale', 'bake')) 





"""
>>> zero_or_one('pale', 'pale')
True
>>> zero_or_one('pale', 'ple')
True
>>> zero_or_one('pales', 'pale') 
True
>>> zero_or_one('pale', 'bale') 
True
>>> zero_or_one('pale', 'bake') 
False
"""


import doctest; doctest.testmod()


True True
True True
True True
True True
False False
Out[19]:
TestResults(failed=0, attempted=5)

1.6 String Compression:

Implement a method to perform basic string compression using the counts of repeated characters. For example, the string aabcccccaaa would become a2blc5a3. If the "compressed" string would not become smaller than the original string, your method should return the original string. You can assume the string has only uppercase and lowercase letters (a - z).


In [12]:
char=''
count=0

def compr(c):
    global char,count
    
    if char == c:
        count+=1
        return ''
 
    old_char, old_count = char, count
    char, count = c, 1
    
    if old_count>0:
        return '{}{}'.format(old_char,old_count)
    
    return ''
    

def compress(string):
    s=''
    for c in string:
        s+=compr(c)
    
    s+=compr('')
    
    return string if len(string) <= len(s) else s



print(compress('xaaabbcdddd  eee '))
print(compress('xaa'))


x1a3b2c1d4 2e3 1
xaa

1.7 Rotate Matrix:

Given an image represented by an NxN matrix, where each pixel in the image is 4 bytes, write a method to rotate the image by 90 degrees. Can you do this in place?


In [79]:
N = 3
m = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]


def generate_m(N):
    '''Generates a square matrix NxN'''
    k = 1
    m = []
    for i in range(0, N):
        n = []
        m.append(n)
        for j in range(0, N):
            n.append(k)
            k += 1
    return m


def print_m(m, N):
    "Pretty prints matrix"
    for i in range(0, N):
        for j in range(0, N):
            print('{0:>4d}'.format(m[i][j]), end='')
        print()
    print()


def rotate(l, n):
    '''Rotates list to the right'''
    return l[n:]+l[:n]


def rotate_m(m, N):
    for i in range(0, N//2+N % 2):
        for j in range(0, N//2):
            cp = [m[i][j], m[N-j-1][i], m[N-i-1][N-j-1], m[j][N-i-1]].copy()
            [m[i][j], m[N-j-1][i], m[N-i-1][N-j-1],
                m[j][N-i-1]] = rotate(cp, 1)


N = 5
m = generate_m(N)
print_m(m, N)

print('rotated:')
rotate_m(m, N)
print_m(m, N)

[x for x in dir() if not x.startswith('__')]


   1   2   3   4   5
   6   7   8   9  10
  11  12  13  14  15
  16  17  18  19  20
  21  22  23  24  25

rotated:
  21  16  11   6   1
  22  17  12   7   2
  23  18  13   8   3
  24  19  14   9   4
  25  20  15  10   5